﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using isky.webapi.requests;

namespace isky.webapi
{
    public partial class CRMWebAPI
    {
        public async Task<CRMGetListResult<ExpandoObject>> GetList(string uri, CRMGetListOptions QueryOptions = null)
        {
            await CheckAuthToken();

            string fullUrl = BuildGetUrl(uri, QueryOptions);
            HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("GET"), fullUrl);

            var preferList = new List<string>();

            if ((QueryOptions != null) && (QueryOptions.FormattedValues))
                preferList.Add("odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");

            if ((QueryOptions != null) && (QueryOptions.TrackChanges))
                preferList.Add("odata.track-changes");

            if (preferList.Count > 0)
                request.Headers.Add("Prefer", string.Join(",", preferList));

            var results = await _httpClient.SendAsync(request);

            EnsureSuccessStatusCode(results);
            var data = await results.Content.ReadAsStringAsync();
            CRMGetListResult<ExpandoObject> resultList = new CRMGetListResult<ExpandoObject>();
            resultList.List = new List<ExpandoObject>();

            var values = JObject.Parse(data);
            var valueList = values["value"].ToList();
            foreach (var value in valueList)
                resultList.List.Add(value.ToObject<ExpandoObject>());

            var deltaLink = values["@odata.deltaLink"];
            if (deltaLink != null)
                resultList.TrackChangesLink = deltaLink.ToString();
            var recordCount = values["@odata.count"];
            if (recordCount != null)
                resultList.Count = int.Parse(recordCount.ToString());
            var nextLink = values["@odata.nextLink"];
            while (nextLink != null)
            {
                var nextResults = await _httpClient.GetAsync(nextLink.ToString());
                EnsureSuccessStatusCode(nextResults);
                var nextData = await nextResults.Content.ReadAsStringAsync();

                var nextValues = JObject.Parse(nextData);
                var nextValueList = nextValues["value"].ToList();
                foreach (var nextvalue in nextValueList)
                    resultList.List.Add(nextvalue.ToObject<ExpandoObject>());
                nextLink = nextValues["@odata.nextLink"];
            }

            return resultList;
        }
        /// <summary>
        /// Retrieve a list of records based on query options
        /// </summary>
        /// <typeparam name="ResultType"></typeparam>
        /// <param name="uri">e.g. accounts</param>
        /// <param name="QueryOptions">Filter, OrderBy,Select, and other options</param>
        /// <returns></returns>
        public async Task<CRMGetListResult<ResultType>> GetList<ResultType>(string uri, CRMGetListOptions QueryOptions = null)
        {
            await CheckAuthToken();

            string fullUrl = BuildGetUrl(uri, QueryOptions);

            HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("GET"), fullUrl);

            if ((QueryOptions != null) && (QueryOptions.FormattedValues))
                request.Headers.Add("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");

            var results = await _httpClient.SendAsync(request);

            EnsureSuccessStatusCode(results);
            var data = await results.Content.ReadAsStringAsync();
            var values = JObject.Parse(data);
            CRMGetListResult<ResultType> resultList = new CRMGetListResult<ResultType>();
            resultList.List = new List<ResultType>();

            foreach (var value in values["value"].ToList())
            {
                resultList.List.Add(value.ToObject<ResultType>());
            }
            var nextLink = values["@odata.nextLink"];
            var recordCount = values["@odata.count"];
            if (recordCount != null)
                resultList.Count = int.Parse(recordCount.ToString());

            while (nextLink != null)
            {
                var nextResults = await _httpClient.GetAsync(nextLink.ToString());
                EnsureSuccessStatusCode(nextResults);
                var nextData = await nextResults.Content.ReadAsStringAsync();

                var nextValues = JObject.Parse(nextData);
                foreach (var value in nextValues["value"].ToList())
                {
                    resultList.List.Add(value.ToObject<ResultType>());
                }
                nextLink = nextValues["@odata.nextLink"];
            }
            return resultList;
        }

        /// <summary>
        /// Get count of matching records.  
        /// 
        /// Note: This returns up to 5,000 records matching criteria it will not reflect all records over 5,000 due to
        /// a limitiation with CRM internal handling of retrieval of the count
        /// </summary>
        /// <param name="uri">e.g. accounts</param>
        /// <param name="QueryOptions">Filter, OrderBy,Select, and other options</param>
        /// <returns></returns>
        public async Task<int> GetCount(string uri, CRMGetListOptions QueryOptions = null)
        {
            await CheckAuthToken();
            if (QueryOptions != null)
                QueryOptions.IncludeCount = false;
            string fullUrl = BuildGetUrl(uri + "/$count", QueryOptions);
            var results = await _httpClient.GetAsync(fullUrl);
            EnsureSuccessStatusCode(results);
            var data = await results.Content.ReadAsStringAsync();

            return int.Parse(data);

        }
        /// <summary>
        /// get a single record matching the URI
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="QueryOptions"></param>
        /// <returns></returns>
        public async Task<ExpandoObject> Get(string entityCollection, Guid entityID, CRMGetListOptions QueryOptions = null)
        {
            return await Get<ExpandoObject>(entityCollection, entityID, QueryOptions);
        }
        /// <summary>
        /// get a single record with the specified return type
        /// </summary>
        /// <typeparam name="ResultType"></typeparam>
        /// <param name="uri"></param>
        /// <param name="QueryOptions"></param>
        /// <returns></returns>
        public async Task<ResultType> Get<ResultType>(string entityCollection, Guid entityID, CRMGetListOptions QueryOptions = null)
        {
            await CheckAuthToken();

            string fullUrl = string.Empty;
            if (entityID == Guid.Empty)
                fullUrl = BuildGetUrl(entityCollection, QueryOptions);
            else
                fullUrl = BuildGetUrl(entityCollection + "(" + entityID.ToString() + ")", QueryOptions);
            HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("GET"), fullUrl);

            if ((QueryOptions != null) && (QueryOptions.FormattedValues))
                request.Headers.Add("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");

            var results = await _httpClient.SendAsync(request);

            EnsureSuccessStatusCode(results);
            var data = await results.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<ResultType>(data);
        }
    }
}
